package app.modules.menu.business.service;

import app.framework.constant.AuthConstants;
import app.modules.account.business.service.IUserService;
import app.modules.account.persistence.entity.UserEO;
import app.modules.common.enums.ShowFlagEnum;
import app.modules.menu.constant.MenuConstants;
import app.modules.menu.dto.biz.MenuFunctionRelationDTO;
import app.modules.menu.dto.dal.MenuDalDTO;
import app.modules.menu.dto.dal.MenuDetailDalDTO;
import app.modules.menu.dto.web.menu.WebHierarchyMenuDTO;
import app.modules.menu.dto.web.menu.WebHomeMenuDTO;
import app.modules.menu.dto.web.menu.WebMenuFunctionDTO;
import app.modules.menu.persistence.dao.MenuDAO;
import app.modules.menu.persistence.dao.MenuFunctionRelationDAO;
import app.modules.menu.util.conversion.MenuConvertUtil;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import jasmine.framework.common.util.CollectionUtil;
import jasmine.framework.common.util.I18nUtil;
import jasmine.framework.common.util.NewUtil;
import jasmine.framework.context.CurrentSubject;
import jasmine.security.rbac.model.SecMenuFunctionRelation;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author mh.z
 */
@Service
public class MenuWithHierarchyService {
    private MenuDAO menuDAO;
    private MenuFunctionRelationDAO menuFunctionRelationDAO;
    private IUserService userService;

    public MenuWithHierarchyService(MenuDAO menuDAO, MenuFunctionRelationDAO menuFunctionRelationDAO,
                                    IUserService userService) {
        this.menuDAO = menuDAO;
        this.menuFunctionRelationDAO = menuFunctionRelationDAO;
        this.userService = userService;
    }

    /**
     * 查询菜单
     *
     * @return
     */
    public List<WebHomeMenuDTO> listWebHomeMenuDTOs() {
        Long userId = CurrentSubject.getUserId();
        if (userId == null) {
            return Collections.emptyList();
        }

        UserEO user = userService.getUserById(userId);
        if (user == null) {
            return Collections.emptyList();
        }

        List<MenuDalDTO> menuDalDtoList = menuDAO.listMenuDalDTOsByTemplateCode(
                MenuConstants.DEFAULT_MENU_TEMPLATE_CODE);
        String userType = user.getUserType();

        if (!AuthConstants.USER_TYPE_SUPER_ADMIN.equals(userType)) {
            // 获取菜单功能关系
            List<SecMenuFunctionRelation> menuFunctionList = menuFunctionRelationDAO
                    .listByTemplateAndUser(MenuConstants.DEFAULT_MENU_TEMPLATE_CODE, userId);
            // 过滤菜单列表
            menuDalDtoList = filterMenusByRelations(menuDalDtoList, menuFunctionList);
        }

        if (CollectionUtil.isEmpty(menuDalDtoList)) {
            return Collections.emptyList();
        }

        Multimap<Long, MenuDalDTO> childrenMultimap = ArrayListMultimap.create();
        menuDalDtoList.forEach((menuDalDTO) -> {
            childrenMultimap.put(menuDalDTO.getParentId(), menuDalDTO);
        });

        List<WebHomeMenuDTO> webMenuDtoList = createWebHomeMenuDTOs(childrenMultimap, null);

        return webMenuDtoList;
    }

    /**
     * 创建 WebHomeMenuDTO 对象
     *
     * @param childrenMultimap
     * @param parentId
     * @return
     */
    protected List<WebHomeMenuDTO> createWebHomeMenuDTOs(Multimap<Long, MenuDalDTO> childrenMultimap,
                                                         Long parentId) {
        Collection<MenuDalDTO> menuDalDtoList = childrenMultimap.get(parentId);

        if (CollectionUtil.isEmpty(menuDalDtoList)) {
            return Collections.emptyList();
        }

        List<WebHomeMenuDTO> webMenuDtoList = CollectionUtil.mapToList(menuDalDtoList, (menuDalDTO) -> {
            WebHomeMenuDTO webMenuDTO = new WebHomeMenuDTO();
            webMenuDTO.setCode(menuDalDTO.getMenuCode());
            webMenuDTO.setName(menuDalDTO.getMenuName());
            webMenuDTO.setIcon(menuDalDTO.getMenuIcon());
            webMenuDTO.setPath(menuDalDTO.getMenuPath());

            List<WebHomeMenuDTO> childList = createWebHomeMenuDTOs(childrenMultimap, menuDalDTO.getId());
            webMenuDTO.setChildren(childList);

            return webMenuDTO;
        });

        return webMenuDtoList;
    }

    /**
     * 查询菜单
     *
     * @param templateId
     * @return
     */
    public List<WebHierarchyMenuDTO> listHierarchyMenus(Long templateId) {
        List<MenuDetailDalDTO> menuList = menuDAO.listMenuDetailDalDTOsByTemplateId(templateId);
        if (CollectionUtil.isEmpty(menuList)) {
            return Collections.emptyList();
        }

        Multimap<Long, MenuDetailDalDTO> childrenMultimap = ArrayListMultimap.create();
        menuList.forEach((menuDTO) -> {
            childrenMultimap.put(menuDTO.getParentId(), menuDTO);
        });

        List<MenuFunctionRelationDTO> relationDtoList = menuFunctionRelationDAO
                .listRelationsByTemplate(templateId);
        Multimap<Long, MenuFunctionRelationDTO> relationsMultimap = ArrayListMultimap.create();
        relationDtoList.forEach((relationDTO) -> {
            relationsMultimap.put(relationDTO.getMenuId(), relationDTO);
        });

        List<WebHierarchyMenuDTO> webMenuDtoList = createWebHierarchyMenuDTOs(childrenMultimap,
                null, relationsMultimap);

        return webMenuDtoList;
    }

    /**
     * 创建 WebHierarchyMenuDTO 对象
     *
     * @param childrenMultimap
     * @param parentId
     * @param relationsMultimap
     * @return
     */
    protected List<WebHierarchyMenuDTO> createWebHierarchyMenuDTOs(Multimap<Long, MenuDetailDalDTO> childrenMultimap,
                                                                   Long parentId, Multimap<Long, MenuFunctionRelationDTO> relationsMultimap) {
        Collection<MenuDetailDalDTO> menuList = childrenMultimap.get(parentId);

        if (CollectionUtil.isEmpty(menuList)) {
            return Collections.emptyList();
        }

        List<WebHierarchyMenuDTO> webMenuDtoList = CollectionUtil.mapToList(menuList, (menuDalDTO) -> {
            Long menuId = menuDalDTO.getId();

            WebHierarchyMenuDTO webMenuDTO = MenuConvertUtil.menuDetailDalDTO2WebHierarchyMenuDTO(menuDalDTO);
            ShowFlagEnum showFlagEnum = ShowFlagEnum.parseValue(webMenuDTO.getShowFlag());
            webMenuDTO.setShowFlagDesc(I18nUtil.getMessage(showFlagEnum));

            // 子级菜单
            List<WebHierarchyMenuDTO> childList = createWebHierarchyMenuDTOs(childrenMultimap, menuId, relationsMultimap);
            if (CollectionUtil.isNotEmpty(childList)) {
                webMenuDTO.setChildMenus(childList);
            }

            // 关联的功能
            Collection<MenuFunctionRelationDTO> relationList = relationsMultimap.get(menuId);
            if (CollectionUtil.isNotEmpty(relationList)) {
                List<WebMenuFunctionDTO> menuFunctionList = CollectionUtil.mapToList(relationList, (relation) -> {
                    WebMenuFunctionDTO menuFunction = new WebMenuFunctionDTO();
                    menuFunction.setId(relation.getFunctionId());
                    menuFunction.setFunctionCode(relation.getFunctionCode());
                    menuFunction.setFunctionName(relation.getFunctionName());

                    return menuFunction;
                });

                webMenuDTO.setChildFunctions(menuFunctionList);
            }

            return webMenuDTO;
        });

        return webMenuDtoList;
    }

    /**
     * 过滤菜单
     *
     * @param menus
     * @param relations
     * @return
     */
    public List<MenuDalDTO> filterMenusByRelations(Collection<MenuDalDTO> menus,
                                                   Collection<SecMenuFunctionRelation> relations) {
        if (CollectionUtil.isEmpty(menus) || CollectionUtil.isEmpty(relations)) {
            return Collections.emptyList();
        }

        Deque<MenuDalDTO> menuQueue = new LinkedList<>();
        Map<Long, MenuDalDTO> menuMap = NewUtil.map(menus.size());
        Set<Long> menuIdSet = NewUtil.set(relations.size());

        menus.forEach((menu) -> {
            menuMap.put(menu.getId(), menu);
        });

        relations.forEach((relation) -> {
            Long menuId = relation.getMenuId();

            if (menuIdSet.add(menuId)) {
                MenuDalDTO menu = menuMap.get(menuId);
                if (menu != null) {
                    menuQueue.add(menu);
                }
            }
        });

        // 沿着父级菜单把菜单ID添加到菜单ID集合中
        while (CollectionUtil.isNotEmpty(menuQueue)) {
            MenuDalDTO menu = menuQueue.pop();

            Long parentId = menu.getParentId();
            if (parentId != null) {
                MenuDalDTO parent = menuMap.get(parentId);

                if (parent != null && menuIdSet.add(parent.getId())) {
                    menuQueue.add(parent);
                }
            }
        }

        List<MenuDalDTO> resultList = NewUtil.list(menus.size());
        menus.forEach((menu) -> {
            if (menuIdSet.contains(menu.getId())) {
                resultList.add(menu);
            }
        });

        return resultList;
    }

}
